using System;
using System.Collections;
using System.Collections.Generic;
using Slaks.Graphs.Algorithms;
using Slaks.Graphs;
using Slaks.Graphs.GraphBuilder;
using DataInput;

namespace Slaks.Graphs.ShenkerGraph.Algorithms
{
	public class ShenkerGraphClassifying : CategorizationBase
    {
        String numberOfNearestNeightboursParam = "NumberOfNearestNeighbours";
	    private int m_numberOfNearsetNeighbours;
        private GraphDistanceBase m_graphDistanceMeasure;

	    public ShenkerGraphClassifying()
        {
        }

        public ShenkerGraphClassifying(GraphList trainingList,
            int numberOfNearesNeighbours, 
            GraphDistanceBase graphDistanceMeasure)
		{
			if (trainingList == null || trainingList.Count == 0) throw new GraphException("There are no graphs in the list");
            if (numberOfNearesNeighbours <= 0) throw new GraphException("Number of clusters must be greater then 0");
            if (trainingList.Count < numberOfNearesNeighbours) numberOfNearesNeighbours = trainingList.Count;

			m_graphDistanceMeasure = graphDistanceMeasure.clone();
            this.m_numberOfNearsetNeighbours = numberOfNearesNeighbours;
            this.m_graphList = trainingList;
		}

        public override List<Field> GetDynamicFields(IRepresentation BT)
        {
            List<Field> fields = base.GetDynamicFields(BT);
            fields.Add(new TextField(numberOfNearestNeightboursParam, "Number of nearest neighbours:", "3", false, true));
            string[] DA = DistanceAlgFactory.Instance.GetSuitableDistanceAlgorithms(BT);
            Dictionary<string, object> distanceAlgorithms = new Dictionary<string, object>();
            for (int i = 0; i < DA.Length; i++)
                distanceAlgorithms.Add(DA[i], DA[i]);
            if (DA.Length > 0)
                fields.Add(new ListField(GraphDistanceBase.DistanceAlgorithmParam, "Distance Algorithm:", DA[0], distanceAlgorithms, false, false));
            return fields;
        }

        public override void SetDynamicValues(IEnumerable<Field> fields)
        {
            base.SetDynamicValues(fields);
            foreach (var field in fields)
            {
                if (field.FieldName == numberOfNearestNeightboursParam)
                    m_numberOfNearsetNeighbours = int.Parse(field.Value.ToString());
                if (field.FieldName == GraphDistanceBase.DistanceAlgorithmParam)
                    m_graphDistanceMeasure = DistanceAlgFactory.Instance.GetInstance(field.Value.ToString());
            }
        }

        
	    /// <summary>
		/// Apply the KNN Algorithm
		/// </summary>
		public override Dictionary<Graph, string> Apply(GraphList testlist, Document2CategoryMap map)
        {
            var results = new Dictionary<Graph, string>(testlist.Count);
            foreach (Graph graph in testlist)
            {

                var distances = new SortedDictionary<double, Graph>();
                foreach (Graph knownGraph in m_graphList)
                {
                    m_graphDistanceMeasure.firstGraph = knownGraph;
                    m_graphDistanceMeasure.secondGraph = graph;
                    double tempDist = m_graphDistanceMeasure.GetDistance (); 
                    while (distances.ContainsKey(tempDist))
                    {
                        tempDist += 0.0000000001;
                    }
                    distances[tempDist] = knownGraph;
                }
                var categories = new Dictionary<string, int>();
                int count = m_numberOfNearsetNeighbours;
                var maxFreq = Int32.MinValue;
                var candidateCategory = string.Empty;
                foreach (KeyValuePair<double, Graph> pair in distances)
                {
                    if (count ==0) break;
                    var category = pair.Value.Category;
                    if (!categories.ContainsKey(category))
                    {
                        categories.Add(category,0);
                    }
                    categories[pair.Value.Category]++;
                    if(maxFreq<categories[pair.Value.Category])
                    {
                        candidateCategory = pair.Value.Category;
                        maxFreq = categories[pair.Value.Category];
                    }
                    count--;
                }
                results[graph] = candidateCategory;
            }
            return results;
        }
	}
}